import {
  HttpException,
  HttpStatus,
  Inject,
  Injectable,
  NotFoundException,
  Scope,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { PaginationQueryDto } from '../../common/dto/pagination-query.dto';
import { Event } from '../../events/entities/event.entity';
import { Connection, QueryRunner, Repository } from 'typeorm';
import { CreateCoffeeDto } from './dto/create-coffee.dto';
import { UpdateCoffeeDto } from './dto/update-coffee.dto';
import { Flavor } from './entities/flavor.entity';
import { Coffee } from './entities/coffee.entity';
import { COFFEE_BRAND } from './coffee.constant';
import { ConfigService, ConfigType } from '@nestjs/config';
import coffeeConfig from './config/coffee.config';

@Injectable({ scope: Scope.REQUEST })
export class CoffeeService {
  /* private coffees: Coffee[] = [
    {
      id: '1',
      name: '咖啡',
      brand: '瑞欣',
      flavors: ['酸甜'],
    },
    {
      id: '2',
      name: '奶茶',
      brand: 'COCO',
      flavors: ['酸甜'],
    },
  ]; */

  constructor(
    @InjectRepository(Coffee)
    private readonly coffeeRepository: Repository<Coffee>,

    @InjectRepository(Flavor)
    private readonly flavorRepository: Repository<Flavor>,

    private readonly connection: Connection,

    @Inject(COFFEE_BRAND)
    private readonly coffeeBrands: string[],

    private readonly configServer: ConfigService,

    @Inject(coffeeConfig.KEY)
    private readonly coffeeConfiguration: ConfigType<typeof coffeeConfig>,
  ) {
    // console.log(this.coffeeBrands);
    // const databaseHost = this.configServer.get<string>(
    //   // 'DATABASE_HOST',
    //   'database.host',
    //   'localhost111', // 第二个参数表示默认值
    // );
    // console.log('databaseHost', databaseHost);
  }

  findAll(paginationQuery: PaginationQueryDto) {
    const { limit, offset } = paginationQuery;
    return this.coffeeRepository.find({
      relations: ['flavors'],
      skip: offset,
      take: limit,
    });
  }

  async findOne(id: string) {
    // const dataItem = await this.coffeeRepository.findOneBy({
    //   id: Number(id)
    // });

    const dataItem = await this.coffeeRepository.find({
      where: {
        id: Number(id),
      },
      relations: ['flavors'],
    });
    if (!dataItem) {
      // throw new NotFoundError
      throw new HttpException(`找不到id为${id}的资源`, HttpStatus.NOT_FOUND);
      // throw new NotFoundException(`找不到id为${id}的资源`);
    }

    return dataItem;
  }

  async create(body: CreateCoffeeDto) {
    const flavors = await this.getFlavors(body.flavors);

    const item = this.coffeeRepository.create({ ...body, flavors });

    return this.coffeeRepository.save(item); // 返回一个promise
  }

  async update(id: string, body: UpdateCoffeeDto) {
    const flavors = await this.getFlavors(body.flavors);

    /* 如果表里面找不到对应的id，则直接返回undefined */
    const item = await this.coffeeRepository.preload({
      id: Number(id),
      ...body,
      flavors,
    });

    if (!item) {
      throw new NotFoundException(`coffee #${id} not found`);
    }
    return this.coffeeRepository.save(item);
  }

  async remove(id: string) {
    const coffee = await this.findOne(id);

    return this.coffeeRepository.remove(coffee);
  }

  async recommend(coffee: Coffee) {
    const queryRunner = this.connection.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      coffee.recommendations++;
      const recommendEvent = new Event();
      recommendEvent.name = 'recommend_coffee';
      recommendEvent.type = 'coffee';
      recommendEvent.payload = {
        coffeeId: coffee.id,
      };

      await queryRunner.manager.save(coffee);
      await queryRunner.manager.save(recommendEvent);

      await queryRunner.commitTransaction();
    } catch (error) {
      // 事务回滚
      await queryRunner.rollbackTransaction();
    } finally {
      // 事务释放
      await queryRunner.release();
    }
  }

  private async preloadFlavorByName(name: string): Promise<Flavor> {
    const existingFlavor = await this.flavorRepository.findOneBy({ name });

    if (existingFlavor) {
      return existingFlavor;
    }

    return this.flavorRepository.create({ name });
  }

  async getFlavors(flavors: string[]) {
    return (
      flavors &&
      (await Promise.all(flavors.map((name) => this.preloadFlavorByName(name))))
    );
  }
}
